1
'****************************** Module Header ******************************'
2 ' Module Name: SocketClient.vb
3 ' Project: VBSL3SocketServer
4 ' Copyright (c) Microsoft Corporation.
6 ' Socket Server application code file, can serve Silverlight and normal socket
9 ' This source is subject to the Microsoft Public License.
10 ' See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
11 ' All other rights reserved.
13 ' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
14 ' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
15 ' WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
16 '***************************************************************************'
18 Imports System
.Collections
.Generic
21 Imports System
.Net
.Sockets
24 Namespace VBSL3SocketServer
25 Public Class SocketMessageEventArgs
27 Private _Error
As Exception
28 Public Property [Error]() As Exception
32 Set(ByVal value
As Exception
)
36 Private _Data
As String
37 Public Property Data() As String
41 Set(ByVal value
As String)
47 Public Class SocketClient
48 ' Define 3 events for async operation:
49 ' Open,Receive and Send
50 Public Event MessageReceived
As EventHandler(Of SocketMessageEventArgs
)
51 Public Event MessageSended
As EventHandler(Of SocketMessageEventArgs
)
52 Public Event ClientConnected
As EventHandler(Of SocketMessageEventArgs
)
54 ' Set receive buffer size
55 Shared ReadOnly BUFFER_SIZE
As Integer = 65536
57 ' Define the End-of-message char, which is used for separating
58 ' byte array into string messages
59 Shared ReadOnly EOM_MARKER
As Char
= ChrW(127)
61 ' The encapsulated socket
62 Private _InnerSocket
As Socket
63 Public Property InnerSocket() As Socket
67 Private Set(ByVal value
As Socket
)
73 Public Sub New(ByVal socket
As Socket
)
74 If socket Is
Nothing Then
75 Throw
New Exception("Socket cannot be null")
79 ' Initialize string decoder
80 encoding
= New UTF8Encoding(False, True)
82 Public Sub New(ByVal addfamily
As AddressFamily
, ByVal socktype
As SocketType
, ByVal protype
As ProtocolType
)
83 InnerSocket
= New Socket(addfamily
, socktype
, protype
)
84 encoding
= New UTF8Encoding(False, True)
87 #Region
"Socket async connect"
89 ' Get socket connect status
90 Public ReadOnly
Property Connected() As Boolean
92 Return InnerSocket
.Connected
102 ''' Connect socket to endpoint asynchronously.
103 ''' Possible exception:
104 ''' ArgumentException
105 ''' ArgumentNullException
106 ''' InvalidOperationException
108 ''' NotSupportedException
109 ''' ObjectDisposedException
110 ''' SecurityException
111 ''' Details at: http://msdn.microsoft.com/en-us/library/bb538102.aspx
113 ''' <param name="ep">remote endpoint</param>
114 Public Sub ConnectAsync(ByVal ep
As EndPoint
)
115 If InnerSocket
.Connected
Then
119 ' Initialize socketAsyncEventArgs
120 ' Set remote connect endpoint
121 Dim connectEventArgs
= New SocketAsyncEventArgs()
122 connectEventArgs
.RemoteEndPoint = ep
123 AddHandler connectEventArgs
.Completed
, AddressOf connectEventArgs_Completed
125 ' Call ConnectAsync method, if method returned false
126 ' it means the result has returned synchronously
127 If Not InnerSocket
.ConnectAsync(connectEventArgs
) Then
128 ' Call method to handle connect result
129 ProcessConnect(connectEventArgs
)
133 ' When connectAsync completed, call method to handle connect result
134 Private Sub connectEventArgs_Completed(ByVal sender
As Object, ByVal e
As SocketAsyncEventArgs
)
138 ' Invoke ClientConnected event to return result
139 Private Sub ProcessConnect(ByVal e
As SocketAsyncEventArgs
)
140 If e
.SocketError
= SocketError
.Success
Then
141 OnClientConnected(Nothing)
143 OnClientConnected(New SocketException(CInt(e
.SocketError
)))
147 Private Sub OnClientConnected(ByVal [error] As Exception
)
148 Dim sockargs
As New SocketMessageEventArgs()
149 sockargs
.Error = [error]
150 RaiseEvent ClientConnected(Me, sockargs
)
154 #Region
"Socket async Send"
157 ''' Use Socket to send string message.
158 ''' Possible exception:
160 ''' ArgumentException
161 ''' InvalidOperationException
162 ''' NotSupportedException
163 ''' ObjectDisposedException
166 ''' <param name="data">message to be sent</param>
167 Public Sub SendAsync(ByVal data
As String)
169 ' If message data contains EOM_MARKER char,
171 If data
.Contains(EOM_MARKER
) Then
172 Throw
New Exception("Unallowed chars existed in message")
175 ' Add End-of-message char at message end.
178 ' Get UTF8 encoded byte array
179 Dim bytesdata
= encoding
.GetBytes(data
)
181 ' Initialize SendEventArgs
182 Dim sendEventArgs
= New SocketAsyncEventArgs()
183 sendEventArgs
.SetBuffer(bytesdata
, 0, bytesdata
.Length
)
184 AddHandler sendEventArgs
.Completed
, AddressOf sendEventArgs_Completed
186 ' Call SendAsync method, if method returned false
187 ' it means the result has returned synchronously
188 If Not InnerSocket
.SendAsync(sendEventArgs
) Then
189 ProcessSend(sendEventArgs
)
193 ' When sendAsync completed, call method to handle send result
194 Private Sub sendEventArgs_Completed(ByVal sender
As Object, ByVal e
As SocketAsyncEventArgs
)
198 ' Invoke MessageSended event to return result
199 Private Sub ProcessSend(ByVal e
As SocketAsyncEventArgs
)
200 If e
.SocketError
= SocketError
.Success
Then
201 OnMessageSended(Nothing)
203 OnMessageSended(New SocketException(CInt(e
.SocketError
)))
207 Private Sub OnMessageSended(ByVal [error] As Exception
)
208 Dim sockargs
As New SocketMessageEventArgs()
209 sockargs
.Error = [error]
210 RaiseEvent MessageSended(Me, sockargs
)
214 #Region
"Socket async Receive"
216 ' Define flag to indicate receive status
217 Private _isReceiving
As Boolean
220 ''' Start receiving bytes from socket and invoke
221 ''' MessageReceived event when each message received.
222 ''' Possible exception:
223 ''' ArgumentException
224 ''' InvalidOperationException
225 ''' NotSupportedException
226 ''' ObjectDisposedException
228 ''' Details at http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.receiveasync.aspx
230 Public Sub StartReceiving()
232 ' Check if socket is started receiving already
233 If Not _isReceiving
Then
240 ' Initialize receiving buffer
241 Dim buffer
= New Byte(BUFFER_SIZE
) {}
243 ' Initialize receive event args
244 Dim receiveEventArgs
= New SocketAsyncEventArgs()
245 receiveEventArgs
.SetBuffer(buffer
, 0, BUFFER_SIZE
)
246 AddHandler receiveEventArgs
.Completed
, AddressOf receiveEventArgs_Completed
248 ' Call ReceiveAsync method, if method returned false
249 ' it means the result has returned synchronously
250 If Not InnerSocket
.ReceiveAsync(receiveEventArgs
) Then
251 ProcessReceive(receiveEventArgs
)
253 Catch ex
As Exception
259 ' Stop receiving bytes from socket
260 Public Sub StopReceiving()
264 Private Sub receiveEventArgs_Completed(ByVal sender
As Object, ByVal e
As SocketAsyncEventArgs
)
268 ' Process receiveAsync complete event
269 Private receivemessage
As String = ""
270 Private encoding
As Encoding
271 Private taillength
As Integer
272 Private Sub ProcessReceive(ByVal e
As SocketAsyncEventArgs
)
273 ' When got Error, invoke MessageReceived event
274 ' to pass the error info to user
275 If e
.SocketError
<> SocketError
.Success
Then
277 OnMessageReceived(Nothing, New SocketException(CInt(e
.SocketError
)))
282 '#Region "String Decoding"
283 ' Decoding bytes to string.
284 ' Note that UTF-8 is variable-length encode, we need check the byte
285 ' array tail in case of separating one character into two.
286 Dim receivestr
As String = ""
289 receivestr
= encoding
.GetString(e
.Buffer
, 0, taillength
+ e
.BytesTransferred
)
290 ' If decode successful, reset tail length
292 Catch ex
As DecoderFallbackException
293 ' If got decode exception, remove the array tail and re decode
295 receivestr
= encoding
.GetString(e
.Buffer
, 0, taillength
+ e
.BytesTransferred
- ex
.BytesUnknown
.Length
)
297 taillength
= ex
.BytesUnknown
.Length
298 ex
.BytesUnknown
.CopyTo(e
.Buffer
, 0)
299 Catch ex2
As DecoderFallbackException
300 ' If still got decode exception, stop receiving.
301 Throw
New Exception("Message decode failed.", ex2
)
306 ' Check if message ended
307 Dim eompos
As Integer = receivestr
.IndexOf(EOM_MARKER
)
309 ' Compose a complete message
310 receivemessage
+= receivestr
.Substring(0, eompos
)
312 ' Notify message received
313 OnMessageReceived(receivemessage
, Nothing)
315 ' Get the remaining string
317 receivestr
= receivestr
.Substring(eompos
+ 1, receivestr
.Length
- eompos
- 1)
319 ' Check if it still has EOM in string
320 eompos
= receivestr
.IndexOf(EOM_MARKER
)
322 receivemessage
+= receivestr
325 If Not _isReceiving
Then
329 ' Reset buffer offset
330 e
.SetBuffer(taillength
, BUFFER_SIZE
- taillength
)
333 If Not InnerSocket
.ReceiveAsync(e
) Then
336 Catch ex
As Exception
337 ' Return error through MessageReceived event
338 OnMessageReceived(Nothing, ex
)
343 Private Sub OnMessageReceived(ByVal data
As String, ByVal [error] As Exception
)
344 Dim sockargs
As New SocketMessageEventArgs()
346 sockargs
.Error = [error]
347 RaiseEvent MessageReceived(Me, sockargs
)